//
//  PLEndpoint.m
//  Plankton
//
//  Created by Marc Schlichte on 2008-05-03.
//  Copyright 2008-2009 Framework Labs. All rights reserved.
//

#import "PLEndpoint.h"

#include <arpa/inet.h>
#include <ifaddrs.h>


#if TARGET_OS_IPHONE
#import <CFNetwork/CFNetwork.h>
#endif

@implementation PLEndpoint

@synthesize port, delegate;

- (void)_notifyProblem:(NSString *)problem
{
    if ([delegate respondsToSelector:@selector(endpoint:didEncounterProblem:)]) {
        [delegate endpoint:self didEncounterProblem:problem];
    }
}

static void HandleSocketData(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
{
    PLEndpoint *endpoint = (PLEndpoint *)info;
    id delegate = endpoint.delegate;
    if ([delegate respondsToSelector:@selector(endpoint:didReceiveData:from:)]) {
        [delegate endpoint:endpoint didReceiveData:(NSData *)data from:(NSData *)address];
    }
}

- (id)init
{
    if (self = [super init]) {
        
        // create a dgram socket
        CFSocketContext ctx = { 0, self, NULL, NULL, NULL };
        sock = CFSocketCreate(NULL, PF_INET, SOCK_DGRAM, 0, kCFSocketDataCallBack, HandleSocketData, &ctx);
        if (!sock) {
            [self _notifyProblem:@"can't create socket"];
            [self release];
            return nil;
        }
        
        // assign own address to socket - specifying 0 for arbitrary port
        struct sockaddr_in saddr;
        saddr.sin_family = AF_INET;
        saddr.sin_port = htons(0);
        saddr.sin_addr.s_addr = INADDR_ANY;
        CFDataRef daddr = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)&saddr, sizeof(saddr), kCFAllocatorNull);
        CFSocketError err = CFSocketSetAddress(sock, daddr);
        CFRelease(daddr);
        if (err) {
            [self _notifyProblem:@"can't set address"];
            [self release];
            return nil;
        }
        
        // extract real port
        daddr = CFSocketCopyAddress(sock);
        struct sockaddr_in *saddrp = (struct sockaddr_in *)CFDataGetBytePtr(daddr);
        port = ntohs(saddrp->sin_port);
        CFRelease(daddr);
        
        // create runloop source and add to current run-loop
        CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(NULL, sock, 0);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
        CFRelease(source);
    }
    return self;
}

- (void)dealloc
{
    if (sock) CFRelease(sock);
    [super dealloc];
}

- (NSString *)getIPAddress
{
	NSString *address = @"127.0.0.1";
	struct ifaddrs *interfaces = NULL;
	struct ifaddrs *temp_addr = NULL;
	int success = 0;
	
	// retrieve the current interfaces - returns 0 on success
	success = getifaddrs(&interfaces);
	if (success == 0)
	{
		// Loop through linked list of interfaces
		temp_addr = interfaces;
		while(temp_addr != NULL)
		{
			if(temp_addr->ifa_addr->sa_family == AF_INET)
			{
				// Check if interface is en0 which is the wifi connection on the iPhone
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
				if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"])
#else
				if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en1"])
#endif
				{
					// Get NSString from C String
					address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
					break;
				}
			}
			
			temp_addr = temp_addr->ifa_next;
		}
	}
		
	// Free memory
	freeifaddrs(interfaces);
	
	return address;
}

- (NSString *)host
{
	return [self getIPAddress];
}

static in_addr_t GetIPForHost(NSString *hostaddr) 
{
	return inet_addr([hostaddr cStringUsingEncoding:NSASCIIStringEncoding]);
}

- (void)sendData:(NSData *)data toHost:(NSString *)host port:(unsigned short)toPort;
{
    in_addr_t ip = GetIPForHost(host);
    if (ip == 0) {
        [self _notifyProblem:[NSString stringWithFormat:@"can't resolve address for host:%@", host]];
        return;
    }
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(toPort);
    saddr.sin_addr.s_addr = ip;
    CFDataRef daddr = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)&saddr, sizeof(saddr), kCFAllocatorNull);
    
    CFSocketError err = CFSocketSendData(sock, daddr, (CFDataRef)data, 0);
    CFRelease(daddr);
    
    if (err) {
        [self _notifyProblem:@"can't send data"];
    }
}

@end

